package math;

public class Vecmath {
    
    private static vec3f origin;

    private Vecmath() {
    }
    
    public static vec3f getOrigin() {
        if (origin == null) {
            origin = new vec3f(0, 0, 0);
        }
        return origin;
    }
    
    public static vec3f rotate(vec3f pt, vec3f axis, float angleRad) {
        return rotate(pt.x, pt.y, pt.z, axis.x, axis.y, axis.z, angleRad);
    }
    public static vec3f rotate(vec3f pt, float u, float v, float w, float angleRad) {
        return rotate(pt.x, pt.y, pt.z, u, v, w, angleRad);
    }
    public static vec3f rotate(float x, float y, float z, float u, float v, float w, float angleRad) {
        float cos = Maths.cosRad(angleRad);
        float sin = Maths.sinRad(angleRad);
        float icos = 1.0f - cos;
        float dp = (x * u + y * v + z * w);
        float vx = u * dp * icos + x * cos + (v * z - w * y) * sin;
        float vy = v * dp * icos + y * cos + (w * x - u * z) * sin;
        float vz = w * dp * icos + z * cos + (u * y - v * x) * sin;
        return new vec3f(vx, vy, vz);
    }

    public static vec3f rotateAboutXAxis(vec3f pt, float angleRad) {
        return rotate(pt, 1, 0, 0, angleRad);
    }

    public static vec3f rotateAboutYAxis(vec3f pt, float angleRad) {
        return rotate(pt, 0, 1, 0, angleRad);
    }

    public static vec3f rotateAboutZAxis(vec3f pt, float angleRad) {
        return rotate(pt, 0, 0, 1, angleRad);
    }

    public static float distanceBetween(vec3f u, vec3f v) {
        double x2 = Maths.pow(u.x - v.x, 2);
        double y2 = Maths.pow(u.y - v.y, 2);
        double z2 = Maths.pow(u.z - v.z, 2);
        return (float) Maths.sqrt(x2 + y2 + z2);
    }

    public static float distanceSqBetween(vec3f u, vec3f v) {
        double x2 = Maths.pow(u.x - v.x, 2);
        double y2 = Maths.pow(u.y - v.y, 2);
        double z2 = Maths.pow(u.z - v.z, 2);
        return (float) (x2 + y2 + z2);
    }

    public static float dotProduct(vec3f u, vec3f v) {
        return (u.x * v.x) + (u.y * v.y) + (u.z * v.z);
    }

    public static float getAngleRadBetween(vec3f u, vec3f v) {
        return Maths.acosRad(dotProduct(u, v) / (u.length() * v.length()));
    }

    public static float getAngleDegBetween(vec3f u, vec3f v) {
        return Maths.acosDeg(dotProduct(u, v) / (u.length() * v.length()));
    }

    public static boolean bBoxContainsInclusive(float l, float r, float b, float t, float f, float n, vec3f vec) {
        if (vec.x < l || vec.x > r) {
            return false;
        }
        if (vec.y < b || vec.y > t) {
            return false;
        }
        if (vec.z < f || vec.z > n) {
            return false;
        }
        return true;
    }

    public static boolean bBoxContainsExclusive(float l, float r, float b, float t, float f, float n, vec3f vec) {
        if (vec.x <= l || vec.x >= r) {
            return false;
        }
        if (vec.y <= b || vec.y >= t) {
            return false;
        }
        if (vec.z <= f || vec.z >= n) {
            return false;
        }
        return true;
    }

    public static vec3f getInverted(vec3f vec) {
        return new vec3f(-vec.x, -vec.y, -vec.z);
    }

    public static vec3f getSurfaceNormal(vec3f pt0, vec3f pt1, vec3f pt2) {
        vec3f ab = subtract(pt0, pt1);
        vec3f ac = subtract(pt0, pt2);
        return crossProduct(ab, ac).getUnitVectorCopy();
    }
    
    public static vec3f getSurfaceNormal(vec3f v0, vec3f v1) {
        return crossProduct(v0, v1).getUnitVectorCopy();
    }

    public static vec3f crossProduct(vec3f u, vec3f v) {
        float x = (u.y * v.z) - (u.z * v.y);
        float y = (u.z * v.x) - (u.x * v.z);
        float z = (u.x * v.y) - (u.y * v.x);
        return new vec3f(x, y, z);
    }

    public static vec3f scalarMult(float scalar, vec3f u) {
        return new vec3f((scalar * u.x), (scalar * u.y), (scalar * u.z));
    }

    public static vec3f add(vec3f u, vec3f v) {
        float x = u.x + v.x;
        float y = u.y + v.y;
        float z = u.z + v.z;
        return new vec3f(x, y, z);
    }

    public static vec3f subtract(vec3f u, vec3f v) {
        float x = u.x - v.x;
        float y = u.y - v.y;
        float z = u.z - v.z;
        return new vec3f(x, y, z);
    }

    public static vec3f getProjectionOfOnto(vec3f u, vec3f v) {
        float k = dotProduct(u, v) / v.lengthSq();
        return scalarMult(k, v);
    }

    public static vec3f sphericalInterp(vec3f u, vec3f v, float t) {
        float angle = getAngleRadBetween(u, v);
        float tangle = t * angle;
        float sinangle = Maths.sinRad(angle);
        float ku = (Maths.sinRad((1 - t) * angle) / sinangle);
        float kv = Maths.sinRad(tangle) / sinangle;
        return add(scalarMult(ku, u), scalarMult(kv, v));
    }

}
